#include <boost/graph/adjacency_list.hpp>

#include <iostream>
#include <string>
#include <map>

#include "GraphTypes.h"

template <typename Graph,
          typename TermIdMap, 
					typename TermFrequencyMap, 
					typename EdgeSectionTypeMap,
					typename EdgeConnectingWordMap
         >
void print_graph(const Graph &graph,
								 TermIdMap term_id_map,
								 TermFrequencyMap term_frequency_map,
								 EdgeSectionTypeMap edge_section_type_map,
								 EdgeConnectingWordMap edge_connecting_word_map
								 );

using namespace boost;

		//edge connecting word property
 typedef boost::property<boost::edge_connecting_word_t,std::string> edge_connecting_word_prop_t;
			typedef boost::adjacency_list<boost::listS,
																	boost::listS,
																	boost::directedS,
																	boost::property<boost::vertex_id_t, long, 
																	 vertex_frequency_prop_t> , //vertex property map
																	boost::property<boost::edge_section_type_t,int,
																	 edge_connecting_word_prop_t > , //edge property map
																	graph_source_prop_t > graph_t;
			/*property maps*/
	typedef boost::property_map<graph_t,boost::vertex_id_t         >::type term_id_map_t;
	typedef boost::property_map<graph_t,boost::vertex_frequency_t     >::type term_frequency_map_t;
	typedef boost::property_map<graph_t,boost::edge_section_type_t    >::type edge_section_type_map_t;
	typedef boost::property_map<graph_t,boost::edge_connecting_word_t >::type edge_connecting_word_map_t;
	typedef boost::property_map<graph_t,boost::graph_name_t           >::type graph_name_map_t;

	

	typedef boost::property_traits<term_id_map_t>::value_type term_id_map_value_t;
	typedef boost::property_traits<term_frequency_map_t>::value_type term_frequency_map_value_t;
	typedef boost::property_traits<edge_section_type_map_t>::value_type edge_section_map_value_t;
	typedef boost::property_traits<edge_connecting_word_map_t>::value_type edge_connecting_word_map_value_t;
	typedef boost::property_traits<graph_name_map_t>::value_type graph_name_map_value_t;

	typedef boost::graph_traits<graph_t>::vertex_descriptor vertex_t;
	typedef boost::graph_traits<graph_t>::edge_descriptor edge_t;
	typedef boost::graph_traits<graph_t>::vertices_size_type size_t;
	typedef boost::graph_traits<graph_t>::out_edge_iterator out_edge_iter_t;
	typedef boost::graph_traits<graph_t>::vertex_iterator vertex_iter_t;

	typedef std::map<long,vertex_t> vertex_map_t;

	template <typename VertexType, typename Map>
	typename VertexType * get_vertex(long id, Map & map);

int main()
{
	
  vertex_map_t vertex_map;
	
	graph_t graph;//size

	term_id_map_t term_id_map = get(vertex_id,graph);
	term_frequency_map_t term_frequency_map = get(vertex_frequency,graph);

	edge_section_type_map_t edge_section_type_map = get(edge_section_type,graph);
	edge_connecting_word_map_t edge_connecting_word_map = get(edge_connecting_word,graph);

	//fill in the graph

  vertex_t vertex;
	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(10,vertex));
	put(vertex_id,graph,vertex,10);
	put(vertex_frequency,graph,vertex,1);

	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(20,vertex));
	put(vertex_id,graph,vertex,20);
	put(vertex_frequency,graph,vertex,4);

	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(30,vertex));
	put(vertex_id,graph,vertex,30);
	put(vertex_frequency,graph,vertex,6);

	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(40,vertex));
	put(vertex_id,graph,vertex,40);
	put(vertex_frequency,graph,vertex,13);

	vertex = add_vertex(graph);
	vertex_map.insert(vertex_map_t::value_type(50,vertex));
	put(vertex_id,graph,vertex,50);
  put(vertex_frequency,graph,vertex,7);

	vertex_t *ver1,*ver2;
	edge_t edge;
	bool inserted;

	if ((ver1 = get_vertex<vertex_t,vertex_map_t>(10,vertex_map))) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver1,graph);
		put(edge_section_type,graph,edge,999);
		put(edge_connecting_word,graph,edge,std::string("slava,kisilevich"));
	}

	if ((ver1 = get_vertex<vertex_t,vertex_map_t>(10,vertex_map))) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver1,graph);
		put(edge_section_type,graph,edge,11111);
		put(edge_connecting_word,graph,edge,std::string("marina,litvak"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(10,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(20,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(edge_section_type,graph,edge,888);
		put(edge_connecting_word,graph,edge,std::string("puki,kuki"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(10,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(30,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(edge_section_type,graph,edge,777);
		put(edge_connecting_word,graph,edge,std::string(""));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(30,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(20,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(edge_section_type,graph,edge,666);
		put(edge_connecting_word,graph,edge,std::string("business"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(30,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(40,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(edge_section_type,graph,edge,555);
		put(edge_connecting_word,graph,edge,std::string("pandemonium"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(20,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(40,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(edge_section_type,graph,edge,444);
		put(edge_connecting_word,graph,edge,std::string("something,nothing,on,the"));
	}

	if ( (ver1 = get_vertex<vertex_t,vertex_map_t>(20,vertex_map) ) && (ver2 = get_vertex<vertex_t,vertex_map_t>(50,vertex_map) )) 
	{
		tie(edge,inserted) = add_edge(*ver1,*ver2,graph);
		put(edge_section_type,graph,edge,333);
		put(edge_connecting_word,graph,edge,std::string("kuki"));
	}

	//print the graph
	print_graph(graph,term_id_map,term_frequency_map,edge_section_type_map,edge_connecting_word_map);
}

template <typename Graph,
          typename TermIdMap, 
					typename TermFrequencyMap, 
					typename EdgeSectionTypeMap,
					typename EdgeConnectingWordMap
         >
void print_graph(const Graph &graph,
								 TermIdMap term_id_map,
								 TermFrequencyMap term_frequency_map,
								 EdgeSectionTypeMap edge_section_type_map,
								 EdgeConnectingWordMap edge_connecting_word_map
								 )
{
  

	term_id_map_value_t term_id_value;
	term_frequency_map_value_t term_frequency_value;
	edge_section_map_value_t edge_section_value;
	edge_connecting_word_map_value_t edge_connecting_word_value;
	graph_name_map_value_t graph_name_value;

   vertex_iter_t vertex_i, vertex_end;
	 tie(vertex_i,vertex_end) = vertices(graph);

	 out_edge_iter_t out_edge_i, out_edge_end;
	 for (; vertex_i != vertex_end; ++vertex_i) //iterate through vertices
	 {
		 term_id_value = get(term_id_map,*vertex_i);
		 term_frequency_value = get(term_frequency_map,*vertex_i);

		 std::cout << "id : " << term_id_value << std::endl;
		 std::cout << "term_frequency_value : " << term_frequency_value << std::endl;

     for (tie(out_edge_i,out_edge_end) = out_edges(*vertex_i,graph); out_edge_i != out_edge_end; ++out_edge_i) //get all out edges of the vertex
		 {
			 vertex_t trgt = target(*out_edge_i,graph);

			 term_id_value = get(term_id_map,trgt);
		   term_frequency_value = get(term_frequency_map,trgt);

			 std::cout << "        connected to id : " << term_id_value << std::endl;
		   std::cout << "        term_frequency_value : " << term_frequency_value << std::endl;

       edge_section_value = get(edge_section_type_map,*out_edge_i);
			 edge_connecting_word_value = get(edge_connecting_word_map,*out_edge_i);

			 std::cout << "        section type : " << edge_section_value << std::endl;
		   std::cout << "        connecting word  : " << edge_connecting_word_value << std::endl;

			 std::cout << "\n";
		 }

		 std::cout << "\n";
	 }
}

template <typename VertexType, typename Map>
typename VertexType * get_vertex(long id, Map & map)
{
	Map::iterator iter = map.find(id);
	if (iter == map.end()) return NULL;
	return &(iter->second);
}